<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Qwen2-VL 对话Demo</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 20px;
        }

        .container {
            background: white;
            border-radius: 20px;
            box-shadow: 0 20px 40px rgba(0,0,0,0.1);
            width: 100%;
            max-width: 800px;
            height: 80vh;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        .header {
            background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
            color: white;
            padding: 20px;
            text-align: center;
        }

        .header h1 {
            font-size: 24px;
            margin-bottom: 5px;
        }

        .header p {
            opacity: 0.9;
            font-size: 14px;
        }

        .config-button {
            position: absolute;
            right: 20px;
            top: 20px;
            background: rgba(255,255,255,0.2);
            color: white;
            border: none;
            border-radius: 50%;
            width: 40px;
            height: 40px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 18px;
            text-decoration: none;
            transition: background-color 0.3s;
        }

        .config-button:hover {
            background: rgba(255,255,255,0.3);
        }

        .chat-container {
            flex: 1;
            display: flex;
            flex-direction: column;
            overflow: hidden;
        }

        .messages {
            flex: 1;
            overflow-y: auto;
            padding: 20px;
            background: #f8f9fa;
        }

        .message {
            margin-bottom: 15px;
            display: flex;
            align-items: flex-start;
            gap: 10px;
        }

        .message.user {
            flex-direction: row-reverse;
        }

        .message-content {
            max-width: 70%;
            padding: 12px 16px;
            border-radius: 18px;
            font-size: 14px;
            line-height: 1.4;
        }

        .message.user .message-content {
            background: #007bff;
            color: white;
        }

        .message.assistant .message-content {
            background: white;
            color: #333;
            border: 1px solid #e0e0e0;
        }

        .message-avatar {
            width: 32px;
            height: 32px;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 12px;
            font-weight: bold;
            color: white;
        }

        .message.user .message-avatar {
            background: #007bff;
        }

        .message.assistant .message-avatar {
            background: #28a745;
        }

        .image-preview {
            max-width: 200px;
            max-height: 200px;
            border-radius: 8px;
            margin-top: 8px;
        }

        .input-area {
            padding: 20px;
            background: white;
            border-top: 1px solid #e0e0e0;
        }

        .input-container {
            display: flex;
            gap: 10px;
            align-items: flex-end;
        }

        .input-group {
            flex: 1;
            position: relative;
        }

        .message-input {
            width: 100%;
            padding: 12px 16px;
            border: 2px solid #e0e0e0;
            border-radius: 25px;
            font-size: 14px;
            resize: none;
            outline: none;
            transition: border-color 0.3s;
            min-height: 44px;
            max-height: 120px;
        }

        .message-input:focus {
            border-color: #007bff;
        }

        .file-input {
            display: none;
        }

        .file-button {
            background: #6c757d;
            color: white;
            border: none;
            border-radius: 50%;
            width: 44px;
            height: 44px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 18px;
            transition: background-color 0.3s;
        }

        .file-button:hover {
            background: #5a6268;
        }

        .send-button {
            background: #007bff;
            color: white;
            border: none;
            border-radius: 50%;
            width: 44px;
            height: 44px;
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 18px;
            transition: background-color 0.3s;
        }

        .send-button:hover {
            background: #0056b3;
        }

        .send-button:disabled {
            background: #ccc;
            cursor: not-allowed;
        }

        .loading {
            display: none;
            text-align: center;
            padding: 10px;
            color: #666;
        }

        .loading.show {
            display: block;
        }

        .error {
            background: #f8d7da;
            color: #721c24;
            padding: 10px;
            border-radius: 8px;
            margin: 10px 0;
            border: 1px solid #f5c6cb;
        }

        .file-preview {
            margin-top: 10px;
            padding: 10px;
            background: #f8f9fa;
            border-radius: 8px;
            border: 1px solid #e0e0e0;
            display: none;
        }

        .file-preview.show {
            display: block;
        }

        .file-preview img {
            max-width: 100px;
            max-height: 100px;
            border-radius: 4px;
        }

        .file-preview .file-name {
            font-size: 12px;
            color: #666;
            margin-top: 5px;
        }

        .remove-file {
            background: #dc3545;
            color: white;
            border: none;
            border-radius: 4px;
            padding: 4px 8px;
            font-size: 12px;
            cursor: pointer;
            margin-left: 10px;
        }



        .annotated-image {
            max-width: 100%;
            border-radius: 8px;
            margin: 10px 0;
            border: 2px solid #28a745;
        }

        .coordinates-info {
            background: #e7f3ff;
            padding: 10px;
            border-radius: 8px;
            margin: 10px 0;
            border-left: 4px solid #007bff;
            font-family: monospace;
            font-size: 12px;
        }

        .detection-controls {
            background: #f8f9fa;
            border-radius: 8px;
            margin: 10px 0;
            border: 1px solid #e0e0e0;
            overflow: hidden;
        }

        .detection-controls-header {
            background: #e9ecef;
            padding: 8px 12px;
            cursor: pointer;
            display: flex;
            justify-content: space-between;
            align-items: center;
            font-size: 12px;
            font-weight: 500;
            color: #495057;
        }

        .detection-controls-header:hover {
            background: #dee2e6;
        }

        .detection-controls-content {
            padding: 10px;
            display: none;
        }

        .detection-controls-content.show {
            display: block;
        }

        .detection-controls label {
            display: block;
            margin-bottom: 5px;
            font-size: 12px;
            color: #666;
        }

        .detection-controls input {
            width: 100%;
            padding: 5px;
            border: 1px solid #ddd;
            border-radius: 4px;
            font-size: 12px;
            margin-bottom: 10px;
        }

        .collapse-arrow {
            transition: transform 0.3s;
        }

        .collapse-arrow.rotated {
            transform: rotate(180deg);
        }

        @media (max-width: 768px) {
            .container {
                height: 100vh;
                border-radius: 0;
            }
            
            .message-content {
                max-width: 85%;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <a href="/public/config.html" class="config-button" title="系统配置">⚙️</a>
            <h1 id="chatTitle">🤖 Qwen2-VL 对话Demo</h1>
            <p>支持文本对话和图片识别</p>
        </div>
        
        <div class="chat-container">
            <div class="messages" id="messages">
                <div class="message assistant">
                    <div class="message-avatar">AI</div>
                    <div class="message-content" id="welcomeMessage">
                        您好！我是Qwen2-VL，可以进行文本对话和图片识别。请发送消息或上传图片开始对话！
                    </div>
                </div>
            </div>
            
            <div class="loading" id="loading">
                AI正在思考中...
            </div>
            
            <div class="input-area">
                <div class="file-preview" id="filePreview">
                    <img id="previewImage" src="" alt="预览图片">
                    <div class="file-name" id="fileName"></div>
                    <button class="remove-file" onclick="removeFile()">移除</button>
                </div>

                <div class="detection-controls" id="detectionControls" style="display: none;">
                    <div class="detection-controls-header" onclick="toggleDetectionControls()">
                        <span>图像尺寸控制</span>
                        <span class="collapse-arrow" id="collapseArrow">▼</span>
                    </div>
                    <div class="detection-controls-content" id="detectionControlsContent">
                        <!-- Qwen2.5-VL使用1000x1000标准化坐标空间，不再需要像素控制参数 -->
                    </div>
                </div>
                
                <div class="input-container">
                    <div class="input-group">
                        <textarea 
                            id="messageInput" 
                            class="message-input" 
                            placeholder="输入您的消息..."
                            rows="1"
                        ></textarea>
                    </div>
                    
                    <input type="file" id="fileInput" class="file-input" accept="image/*">
                    <button class="file-button" onclick="document.getElementById('fileInput').click()" title="上传图片">
                        📎
                    </button>
                    
                    <button class="send-button" id="sendButton" onclick="sendMessage()" title="发送消息">
                        ➤
                    </button>
                </div>
            </div>
        </div>
    </div>

    <script>
        let chatHistory = [];
        let selectedFile = null;
        let currentConfig = null;

        // 默认配置
        const defaultConfig = {
            temperature: 0.7,
            maxTokens: 2000,
            systemPrompt: "你是一个有用的AI助手，可以回答问题和协助用户完成各种任务。",
            chatTitle: "🤖 Qwen2-VL 对话Demo",
            welcomeMessage: "您好！我是Qwen2-VL，可以进行文本对话和图片识别。请发送消息或上传图片开始对话！"
        };

        // 加载配置
        function loadConfig() {
            currentConfig = JSON.parse(localStorage.getItem('qwen2vl_config') || JSON.stringify(defaultConfig));
            
            // 应用配置到界面
            document.getElementById('chatTitle').textContent = currentConfig.chatTitle;
            document.getElementById('welcomeMessage').textContent = currentConfig.welcomeMessage;
            
            return currentConfig;
        }

        // 自动调整输入框高度
        document.getElementById('messageInput').addEventListener('input', function() {
            this.style.height = 'auto';
            this.style.height = Math.min(this.scrollHeight, 120) + 'px';
        });

        // 回车发送消息
        document.getElementById('messageInput').addEventListener('keydown', function(e) {
            if (e.key === 'Enter' && !e.shiftKey) {
                e.preventDefault();
                sendMessage();
            }
        });

        // 初始化
        document.addEventListener('DOMContentLoaded', function() {
            loadConfig();
        });

        // 文件选择处理
        document.getElementById('fileInput').addEventListener('change', function(e) {
            const file = e.target.files[0];
            if (file) {
                selectedFile = file;
                showFilePreview(file);
            }
        });

        function showFilePreview(file) {
            const preview = document.getElementById('filePreview');
            const previewImage = document.getElementById('previewImage');
            const fileName = document.getElementById('fileName');
            const detectionControls = document.getElementById('detectionControls');
            
            const reader = new FileReader();
            reader.onload = function(e) {
                previewImage.src = e.target.result;
                fileName.textContent = file.name;
                preview.classList.add('show');
                
                // 显示图像尺寸控制面板
                detectionControls.style.display = 'block';
            };
            reader.readAsDataURL(file);
        }

        function removeFile() {
            selectedFile = null;
            document.getElementById('fileInput').value = '';
            document.getElementById('filePreview').classList.remove('show');
            document.getElementById('detectionControls').style.display = 'none';
        }

        function addMessage(role, content, imageUrl = null) {
            const messagesContainer = document.getElementById('messages');
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${role}`;
            
            const avatar = document.createElement('div');
            avatar.className = 'message-avatar';
            avatar.textContent = role === 'user' ? '我' : 'AI';
            
            const contentDiv = document.createElement('div');
            contentDiv.className = 'message-content';
            
            if (imageUrl) {
                const img = document.createElement('img');
                img.src = imageUrl;
                img.className = 'image-preview';
                contentDiv.appendChild(img);
            }
            
            if (content) {
                const textDiv = document.createElement('div');
                textDiv.textContent = content;
                contentDiv.appendChild(textDiv);
            }
            
            messageDiv.appendChild(avatar);
            messageDiv.appendChild(contentDiv);
            messagesContainer.appendChild(messageDiv);
            
            // 滚动到底部
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
        }

        function showError(message) {
            const messagesContainer = document.getElementById('messages');
            const errorDiv = document.createElement('div');
            errorDiv.className = 'error';
            errorDiv.textContent = message;
            messagesContainer.appendChild(errorDiv);
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
        }

        function showLoading(show) {
            const loading = document.getElementById('loading');
            const sendButton = document.getElementById('sendButton');
            
            if (show) {
                loading.classList.add('show');
                sendButton.disabled = true;
            } else {
                loading.classList.remove('show');
                sendButton.disabled = false;
            }
        }

        async function sendMessage() {
            const messageInput = document.getElementById('messageInput');
            const message = messageInput.value.trim();
            
            if (!message && !selectedFile) {
                return;
            }

            // 显示用户消息
            let imageUrl = null;
            if (selectedFile) {
                const reader = new FileReader();
                reader.onload = function(e) {
                    imageUrl = e.target.result;
                    addMessage('user', message, imageUrl);
                };
                reader.readAsDataURL(selectedFile);
            } else {
                addMessage('user', message);
            }

            // 清空输入
            messageInput.value = '';
            messageInput.style.height = 'auto';
            
            // 显示加载状态
            showLoading(true);

            try {
                let response;
                
                if (selectedFile) {
                    // 发送图片+文本
                    const formData = new FormData();
                    formData.append('image', selectedFile);
                    formData.append('message', message);
                    formData.append('history', JSON.stringify(chatHistory));
                    formData.append('config', JSON.stringify(currentConfig));
                    
                    response = await fetch('/api/chat-with-image', {
                        method: 'POST',
                        body: formData
                    });
                } else {
                    // 发送纯文本
                    response = await fetch('/api/chat', {
                        method: 'POST',
                        headers: {
                            'Content-Type': 'application/json'
                        },
                        body: JSON.stringify({
                            message: message,
                            history: chatHistory,
                            config: currentConfig
                        })
                    });
                }

                const data = await response.json();
                
                if (data.success) {
                    // 显示AI回复
                    addMessage('assistant', data.response);
                    
                    // 检查是否为多面体检测模式且响应中包含坐标
                    const isPolyhedronMode = currentConfig && currentConfig.systemPrompt && 
                                           currentConfig.systemPrompt.includes('多面体') && 
                                           currentConfig.systemPrompt.includes('coordinates');
                    
                    if (isPolyhedronMode && selectedFile) {
                        // 尝试解析响应中的坐标
                        const coordinates = parseCoordinatesFromResponse(data.response);
                        if (coordinates && coordinates.length > 0) {
                            // 如果找到坐标，进行图像标注
                            await processPolyhedronDetection(selectedFile, coordinates, data.response);
                        }
                    }
                    
                    // 更新对话历史
                    if (selectedFile) {
                        chatHistory.push({
                            role: 'user',
                            content: [
                                { type: 'text', text: message },
                                { type: 'image_url', image_url: { url: imageUrl } }
                            ]
                        });
                    } else {
                        chatHistory.push({ role: 'user', content: message });
                    }
                    chatHistory.push({ role: 'assistant', content: data.response });
                    
                    // 限制历史记录长度
                    if (chatHistory.length > 20) {
                        chatHistory = chatHistory.slice(-20);
                    }
                } else {
                    showError('错误: ' + data.error);
                }
            } catch (error) {
                showError('网络错误: ' + error.message);
            } finally {
                showLoading(false);
                removeFile();
            }
        }

        function addAnnotatedMessage(role, content, imageUrl = null, coordinates = null) {
            const messagesContainer = document.getElementById('messages');
            const messageDiv = document.createElement('div');
            messageDiv.className = `message ${role}`;
            
            const avatar = document.createElement('div');
            avatar.className = 'message-avatar';
            avatar.textContent = role === 'user' ? '我' : 'AI';
            
            const contentDiv = document.createElement('div');
            contentDiv.className = 'message-content';
            
            if (imageUrl) {
                const img = document.createElement('img');
                img.src = imageUrl;
                img.className = 'annotated-image';
                contentDiv.appendChild(img);
            }
            
            if (coordinates && coordinates.length > 0) {
                const coordDiv = document.createElement('div');
                coordDiv.className = 'coordinates-info';
                coordDiv.innerHTML = `<strong>检测到的坐标:</strong><br>${JSON.stringify(coordinates, null, 2)}`;
                contentDiv.appendChild(coordDiv);
            }
            
            if (content) {
                const textDiv = document.createElement('div');
                textDiv.textContent = content;
                contentDiv.appendChild(textDiv);
            }
            
            messageDiv.appendChild(avatar);
            messageDiv.appendChild(contentDiv);
            messagesContainer.appendChild(messageDiv);
            
            // 滚动到底部
            messagesContainer.scrollTop = messagesContainer.scrollHeight;
        }

        async function detectPolyhedron() {
            if (!selectedFile) {
                showError('请先上传图片');
                return;
            }

            const polyhedronButton = document.getElementById('polyhedronButton');
            const originalText = polyhedronButton.innerHTML;
            
            try {
                // 显示加载状态
                polyhedronButton.disabled = true;
                polyhedronButton.innerHTML = '⏳';
                showLoading(true);

                // 构建FormData - Qwen2.5-VL使用1000x1000标准化坐标空间
                const formData = new FormData();
                formData.append('image', selectedFile);
                formData.append('config', JSON.stringify(currentConfig));

                // 发送请求
                const response = await fetch('/api/detect-polyhedron', {
                    method: 'POST',
                    body: formData
                });

                const data = await response.json();
                
                if (data.success) {
                    // 显示原始响应
                    addMessage('assistant', data.response);
                    
                    // 如果有标注图片，显示标注结果
                    if (data.annotated_image) {
                        addAnnotatedMessage('assistant', '多面体检测结果（已标注）:', data.annotated_image, data.coordinates);
                    } else if (data.coordinates) {
                        addAnnotatedMessage('assistant', '检测到多面体坐标:', null, data.coordinates);
                    }
                    
                    // 更新对话历史
                    const reader = new FileReader();
                    reader.onload = function(e) {
                        const imageUrl = e.target.result;
                        chatHistory.push({
                            role: 'user',
                            content: [
                                { type: 'text', text: '检测图片中的多面体' },
                                { type: 'image_url', image_url: { url: imageUrl } }
                            ]
                        });
                        chatHistory.push({ role: 'assistant', content: data.response });
                        
                        // 限制历史记录长度
                        if (chatHistory.length > 20) {
                            chatHistory = chatHistory.slice(-20);
                        }
                    };
                    reader.readAsDataURL(selectedFile);
                    
                } else {
                    showError('多面体检测失败: ' + data.error);
                }
            } catch (error) {
                showError('网络错误: ' + error.message);
            } finally {
                // 恢复按钮状态
                polyhedronButton.disabled = false;
                polyhedronButton.innerHTML = originalText;
                                 showLoading(false);
             }
         }

         function toggleDetectionControls() {
             const content = document.getElementById('detectionControlsContent');
             const arrow = document.getElementById('collapseArrow');
             
             if (content.classList.contains('show')) {
                 content.classList.remove('show');
                 arrow.classList.remove('rotated');
             } else {
                 content.classList.add('show');
                 arrow.classList.add('rotated');
             }
         }

         function parseCoordinatesFromResponse(responseText) {
             // 从模型响应中解析坐标信息
             try {
                 // 尝试匹配JSON格式的坐标
                 const jsonPattern = /\{[^{}]*"coordinates"[^{}]*\}/g;
                 const jsonMatches = responseText.match(jsonPattern);
                 
                 if (jsonMatches) {
                     for (let match of jsonMatches) {
                         try {
                             const coordData = JSON.parse(match);
                             if (coordData.coordinates) {
                                 return coordData.coordinates;
                             }
                         } catch (e) {
                             continue;
                         }
                     }
                 }
                 
                 // 尝试匹配数组格式的坐标 [x1, y1, x2, y2]
                 const arrayPattern = /\[(\d+),\s*(\d+),\s*(\d+),\s*(\d+)\]/g;
                 const arrayMatches = [...responseText.matchAll(arrayPattern)];
                 
                 if (arrayMatches.length > 0) {
                     return arrayMatches.map(match => [
                         parseInt(match[1]), parseInt(match[2]), 
                         parseInt(match[3]), parseInt(match[4])
                     ]);
                 }
                 
                 // 尝试匹配bbox格式
                 const bboxPattern = /<box>\((\d+),(\d+)\),\((\d+),(\d+)\)<\/box>/g;
                 const bboxMatches = [...responseText.matchAll(bboxPattern)];
                 
                 if (bboxMatches.length > 0) {
                     return bboxMatches.map(match => [
                         parseInt(match[1]), parseInt(match[2]), 
                         parseInt(match[3]), parseInt(match[4])
                     ]);
                 }
                 
                 return null;
             } catch (error) {
                 console.error('坐标解析失败:', error);
                 return null;
             }
         }

         // 无需复杂resize计算，Qwen2.5-VL使用1000x1000标准化坐标空间
         
         function convertCoordinatesToOriginal(coordinates, originalWidth, originalHeight) {
             // Qwen2.5-VL使用1000x1000标准化坐标空间
             return coordinates.map(coord => {
                 if (coord.length >= 4) {
                     const [x1, y1, x2, y2] = coord;
                     return [
                         Math.round(x1 * originalWidth / 1000),
                         Math.round(y1 * originalHeight / 1000),
                         Math.round(x2 * originalWidth / 1000),
                         Math.round(y2 * originalHeight / 1000)
                     ];
                 }
                 return coord;
             });
         }

         async function processPolyhedronDetection(imageFile, coordinates, responseText) {
             // 处理多面体检测，生成标注图片
             try {
                 // 创建canvas来绘制标注
                 const canvas = document.createElement('canvas');
                 const ctx = canvas.getContext('2d');
                 
                 // 创建Image对象加载原图
                 const img = new Image();
                 
                 return new Promise((resolve) => {
                     img.onload = function() {
                         // 设置canvas尺寸
                         canvas.width = img.width;
                         canvas.height = img.height;
                         
                         // 转换坐标：从1000x1000标准化空间到原始图像坐标
                         const convertedCoordinates = convertCoordinatesToOriginal(coordinates, img.width, img.height);
                         
                         // 输出调试信息
                         console.log('[前端坐标转换] 原始尺寸:', img.width + 'x' + img.height);
                         console.log('[前端坐标转换] 坐标系统: 1000x1000标准化空间');
                         console.log('[前端坐标转换] AI返回坐标:', coordinates);
                         console.log('[前端坐标转换] 转换后坐标:', convertedCoordinates);
                         
                         // 绘制原图
                         ctx.drawImage(img, 0, 0);
                         
                         // 绘制边界框
                         ctx.strokeStyle = 'red';
                         ctx.lineWidth = 3;
                         
                         convertedCoordinates.forEach(coord => {
                             if (coord.length >= 4) {
                                 const [x1, y1, x2, y2] = coord;
                                 // 确保坐标在图像范围内
                                 const clampedX1 = Math.max(0, Math.min(x1, img.width));
                                 const clampedY1 = Math.max(0, Math.min(y1, img.height));
                                 const clampedX2 = Math.max(0, Math.min(x2, img.width));
                                 const clampedY2 = Math.max(0, Math.min(y2, img.height));
                                 
                                 ctx.strokeRect(clampedX1, clampedY1, clampedX2 - clampedX1, clampedY2 - clampedY1);
                             }
                         });
                         
                         // 转换为base64并显示
                         const annotatedImageUrl = canvas.toDataURL('image/png');
                         addAnnotatedMessage('assistant', '多面体检测结果（已标注）:', annotatedImageUrl, convertedCoordinates);
                         
                         resolve();
                     };
                     
                     // 读取图片文件
                     const reader = new FileReader();
                     reader.onload = function(e) {
                         img.src = e.target.result;
                     };
                     reader.readAsDataURL(imageFile);
                 });
             } catch (error) {
                 console.error('图像标注失败:', error);
             }
         }
    </script>
</body>
</html> 